Option Explicit

'################################################################
' Discriptor
' DataFrame 过滤器
'################################################################


'################################################################
' Field
'################################################################
'---------------------------
' 数据源
'---------------------------
' 当前过滤器包含的 DataFrame
Private mainDF As IDataFrame

' 当前 mainDF 经单列条件筛选后的可用行索引
' 通过 unsafeSetMainDFUsableRowIndexes 设置
Private mainDFUsableRowIndexes As IDFSeries

'---------------------------
' 活动行号
'---------------------------
' 当前 DFFilter 中正在访问的活动行号
' 保存的是 mainDF 中的真实行索引
Private activeRowIndex As Long

' 缓存当前 activeRowIndex 的值
' 因为遇到 join 连接失败的时候，会将 activeRowIndex 设置为 -11
' 操作完成之后，需要通过这个 Backup 来还原
Private activeRowIndexBackup As Long

'---------------------------
' Filter 过滤器语义数据
'---------------------------
' 保存 DF 的别名
Private fieldAliasName As String

' 用于分组的列
' HashSet<IDFFilterCol>
'
' 如果不调用 `groupBy` 方法添加分组列，则默认为 Nothing
Private groupByCols As ISet

' 保存当前 DataFrame 中各个列的方法生成器的引用
' Map<"mainDF中的列索引", IDFFilterCol>
'
' 初始化时创建，第一次尝试从当前 Filter 中获取列X时，创建并缓存
Private colMethodCreatorCache As Object

' 如果是 DFFilterSingleCol，则最终只保留列的引用
' 如果是其他类型的列，需要手动计算结果并保存
'
' 第一次调用 select 方法时，生成该集合对象
' 最终生成结果时，如果当前对象是 Nothing，则生成所有关联 DataFrame 中的所有列的引用 !!!!!
'
' List<IDFFilterCol>
'
' 如果不调用 `selectCol` 方法添加分组列，则默认为 Nothing
Private selectCols As Collection

' 保存与当前 DataFrame 有关的 where 条件
'
' 如果不调用 `where` 方法添加分组列，则默认为 Nothing
Private mainConditionExecutor As DFFilterMainConditionExecutor

' 保存所有 join 连接的相关数据
' List<IDFFilterJoinExecutor>
'
' 如果不调用 `leftJoin`, `rightJoin`, `innerJoin` 方法添加分组列，则默认为 Nothing
Private joinExecutors As Collection

'---------------------------
' 缓存数据
'---------------------------
' 当前的活动 join Filter 缓存，用来在生成条件阶段，负责记录信息
' 每次调用 Join 方法时，需要重新生成
Private activeJoinExecutor As IDFFilterJoinExecutor


'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
End Sub

Private Sub Class_Terminate()
    Set colMethodCreatorCache = Nothing
End Sub

'################################################################
' Init
'################################################################
Public Sub Init(ByRef df As IDataFrame)
    Set mainDF = df
    Set colMethodCreatorCache = UtilsCreateSysObj.createMap
End Sub

'################################################################
' Property
'################################################################
' mainDF 中的数据数量
Public Property Get MainDFSize() As Long
    MainDFSize = mainDF.Size
End Property

' mainDF 中的可用数据数量
' 如果已经设置了 mainDFUsableRowIndexes，则返回 mainDFUsableRowIndexes 的长度
' 如果没有设置 mainDFUsableRowIndexes，则返回 mainDF 的数据长度
Public Property Get UsableSize() As Long
    If mainDFUsableRowIndexes Is Nothing Then
        UsableSize = mainDF.Size
    Else
        UsableSize = mainDFUsableRowIndexes.Size
    End If
End Property

' 返回列名
Public Property Get Name() As String
    ' 默认返回 DataFrame 的ID
    Name = mainDF.ID
End Property

' 返回别名
' 如果没有设置别名，则返回 Name
Public Property Get AliasName() As String
    If fieldAliasName = "" Then
        AliasName = mainDF.ID
    Else
        AliasName = fieldAliasName
    End If
End Property

' 返回别名
' 如果没有设置别名，则返回 Name
Public Property Get MainDataFrame() As IDataFrame
    Set MainDataFrame = mainDF
End Property

'################################################################
' Function
'################################################################
'---------------------------
' 设置有效行索引
'---------------------------
Public Sub unsafeSetMainDFUsableRowIndexes(ByRef indexSeries As IDFSeries)
    ' 1. 检查参数
    If indexSeries Is Nothing Then
        Err.Raise 10001, Description:="parameter indexSeries must be not nothing"
    End If

    ' 2. 保存索引引用
    Set mainDFUsableRowIndexes = indexSeries
End Sub

'---------------------------
' 读写活动行号
'---------------------------
' 获取当前的活动行号
' 返回的是 DataFrame 下的真实索引
'
' 省略的索引范围检查，外部不应该主动调用
Public Function unsafeGetActiveRowIndex() As Long
    unsafeGetActiveRowIndex = activeRowIndex
End Function

' 设置当前的活动行索引
' 设置的是 DataFrame 下的真实索引
'
' 省略的索引范围检查，外部不应该主动调用
' 使用时，应该根据 MainDFSize 的范围来设置
Public Sub unsafeSetActiveRowIndex(ByRef i As Long)
    activeRowIndex = i
    activeRowIndexBackup = i
End Sub

' 设置当前的活动行索引
' 如果 mainDFUsableRowIndexes 不是空集合，则自动访问集合，将 i 转换成真实的 DataFrame 索引
' 如果 mainDFUsableRowIndexes 是空集合，则直接将 i 当作真实 DataFrame 的索引进行保存
'
' 省略的索引范围检查，外部不应该主动调用
' 使用时，应该根据 UsableSize 的范围来设置
Public Sub unsafeSetActiveUsableRowIndex(ByRef i As Long)
    If mainDFUsableRowIndexes Is Nothing Then
        activeRowIndex = i
        activeRowIndexBackup = i
    Else
        activeRowIndex = mainDFUsableRowIndexes.getData(i)
        activeRowIndexBackup = activeRowIndex
    End If
End Sub

' 设置当前的活动行索引设置为空行
Public Sub unsafeSetActiveRowIndexIsEmpty()
    activeRowIndex = DFHelper.SERIES_SLICE_EMPTY_REF_SLOT_VAL
End Sub

' 恢复当前的活动行索引
Public Sub unsafeResetActiveRowIndex()
    activeRowIndex = activeRowIndexBackup
End Sub

' ------------------------------------------------------
' Filter 过滤器语义操作
' ------------------------------------------------------
' ==================================
' 原地操作
' ==================================
' 为当前 DFFilter 设置别名
' @param name 别名
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function alias(ByRef name As String) As DFFilter
    fieldAliasName = name
    Set alias = Me
End Function

' ==================================
' join
' ==================================
' 添加一个 LeftJoin
' 
' @param filter 需要结合的过滤器
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function leftJoin(ByRef filter As DFFilter) As DFFilter
    ' 1. 检查参数
    If filter Is Nothing Then
        Err.Raise 10001, Description:="parameter filter must be not nothing"
        Exit Function
    End If

    ' 2. 创建缓存
    If joinExecutors Is Nothing Then
        Set joinExecutors = New Collection
    End If

    ' 3. 创建 leftJoin 执行器并缓存
    Dim leftJoinExecutor As DFFilterLeftJoinExecutor
    Set leftJoinExecutor = New DFFilterLeftJoinExecutor
    leftJoinExecutor.Init filter

    joinExecutors.Add leftJoinExecutor

    ' 4. 将当前的执行器设置为活动 join 执行器
    Set activeJoinExecutor = leftJoinExecutor

    ' 5. 返回自身，完成链式调用
    Set leftJoin = Me
End Function

' 添加一个 RightJoin
' 
' @param filter 需要结合的过滤器
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function rightJoin(ByRef filter As DFFilter) As DFFilter
    ' 1. 检查参数
    If filter Is Nothing Then
        Err.Raise 10001, Description:="parameter filter must be not nothing"
        Exit Function
    End If

    ' 2. 创建缓存
    If joinExecutors Is Nothing Then
        Set joinExecutors = New Collection
    End If

    ' 3. 创建 rightJoin 执行器并缓存
    Dim rightJoinExecutor As DFFilterRightJoinExecutor
    Set rightJoinExecutor = New DFFilterRightJoinExecutor
    rightJoinExecutor.Init filter

    joinExecutors.Add rightJoinExecutor

    ' 4. 将当前的执行器设置为活动 join 执行器
    Set activeJoinExecutor = rightJoinExecutor

    ' 5. rightJoin
    Set rightJoin = Me
End Function

' 添加一个 InnerJoin
' 
' @param filter 需要结合的过滤器
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function innerJoin(ByRef filter As DFFilter) As DFFilter
    ' 1. 检查参数
    If filter Is Nothing Then
        Err.Raise 10001, Description:="parameter filter must be not nothing"
        Exit Function
    End If

    ' 2. 创建缓存
    If joinExecutors Is Nothing Then
        Set joinExecutors = New Collection
    End If

    ' 3. 创建 innerJoin 执行器并缓存
    Dim innerJoinExecutor As DFFilterInnerJoinExecutor
    Set innerJoinExecutor = New DFFilterInnerJoinExecutor
    innerJoinExecutor.Init filter

    joinExecutors.Add innerJoinExecutor

    ' 4. 将当前的执行器设置为活动 join 执行器
    Set activeJoinExecutor = innerJoinExecutor

    ' 5. 返回自身，完成链式调用
    Set innerJoin = Me
End Function

' 添加 join And 条件
' 添加条件前，必须调用 leftJoin, rightJoin, innerJoin 来创建一个连接执行器
'
' @param c 条件对象
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function joinON(ByRef c As IDFFilterColCondition) As DFFilter
    ' 1. 委托给 joinAnd
    joinAND c

    ' 2. 返回自身，完成链式调用
    Set joinON = Me
End Function

' 添加 join And 条件
' 添加条件前，必须调用 leftJoin, rightJoin, innerJoin 来创建一个连接执行器
'
' @param c 条件对象
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function joinAND(ByRef c As IDFFilterColCondition) As DFFilter
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    ' 2. 必须存在一个活动 join 执行器
    If activeJoinExecutor Is Nothing Then
        Err.Raise 10303, Description:="Can not add join condition, because not exist joinExecutor. Please call method: leftJoin/rightJoin/innerJoin first."
        Exit Function
    End If

    ' 3. 保存 join 条件
    activeJoinExecutor.joinAND c

    ' 4. 返回自身，完成链式调用
    Set joinAND = Me
End Function

' 添加 join Or 条件
' 添加条件前，必须调用 leftJoin, rightJoin, innerJoin 来创建一个连接执行器
'
' @param c 条件对象
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function joinOR(ByRef c As IDFFilterColCondition) As DFFilter
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    ' 2. 必须存在一个活动 join 执行器
    If activeJoinExecutor Is Nothing Then
        Err.Raise 10303, Description:="Can not add join condition, because not exist joinExecutor. Please call method: leftJoin/rightJoin/innerJoin first."
        Exit Function
    End If

    ' 3. 保存 join 条件
    activeJoinExecutor.joinOR c

    ' 4. 返回自身，完成链式调用
    Set joinOR = Me
End Function

' ==================================
' where
' ==================================
' 为当前 df 创建 where 条件
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function where() As DFFilter
    Set mainConditionExecutor = New DFFilterMainConditionExecutor
    mainConditionExecutor.Init Me

    Set where = Me
End Function

' 添加 where And 条件
' 必须先调用 `where()` 生成 DFWhereExecutor
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function whereAND(ByRef c As IDFFilterColCondition) As DFFilter
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    ' 2. 必须存在执行器
    If mainConditionExecutor Is Nothing Then
        Err.Raise 10304, Description:="Can not add where condition, because not exist conditionExecutor. Please call method: where first."
        Exit Function
    End If

    ' 3. 保存条件
    mainConditionExecutor.cAND c

    ' 4. 返回自身，完成链式调用
    Set whereAND = Me
End Function

' 添加 where Or 条件
' 必须先调用 `where()` 生成 DFWhereExecutor
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function whereOR(ByRef c As IDFFilterColCondition) As DFFilter
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    ' 2. 必须存在执行器
    If mainConditionExecutor Is Nothing Then
        Err.Raise 10304, Description:="Can not add where condition, because not exist conditionExecutor. Please call method: where first."
        Exit Function
    End If

    ' 3. 保存条件
    mainConditionExecutor.cOR c

    ' 4. 返回自身，完成链式调用
    Set whereOR = Me
End Function

' ==================================
' 获取列
' ==================================
' 获取/创建列对象
' @param index 列名或列索引
' @return 列对象
Public Function col(ByRef index As Variant) As IDFFilterCol
    ' 1. 获取真实的列索引
    Dim indexVal As Integer
    If VarType(index) = vbString Then
        ' 如果索引是字符串，则作为列名使用
        indexVal = mainDF.colNameToIndex(index & "")
    ElseIf IsNumeric(index) Then
        ' 作为列索引使用
        indexVal = Int(index)
        If indexVal < 0 Or indexVal >= mainDF.ColumnCount Then
            Err.Raise 10210, Description:="parameter colum index=" & indexVal & " out of bounds. min index=0, max index[" & (mainDF.ColumnCount - 1) & "]"
            Exit Function
        End If
    Else
        Err.Raise 10012, Description:="please user Integer Or String for parameter"
    End If

    ' 2. 尝试从缓存中获取方法生成器
    If colMethodCreatorCache.Exists(indexVal) Then
        Set col = colMethodCreatorCache.Item(indexVal)
        Exit Function
    End If

    ' 3. 如果缓存中没有，则进行手动创建，并缓存
    Dim result As DFFilterSingleCol
    Set result = New DFFilterSingleCol
    result.Init Me, indexVal, mainDF.colIndexToName(indexVal), mainDF.getCol(indexVal)

    colMethodCreatorCache.Add indexVal, result

    ' 4. 返回列对象
    Set col = result
End Function

' 获取/创建 DataFrame 下的所有列对象
' @return 列对象集合 List<IDFFilterCol>
Public Function allCols() As Collection
    Dim result As Collection
    Set result = New Collection

    Dim name As Variant
    For Each name In mainDF.ColumnNames
        result.Add col(name)
    Next

    Set allCols = result
End Function

' ==================================
' 筛选列
' ==================================
' 添加筛选列
' @param c 需要添加的列
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function selectCol(ByRef c As IDFFilterCol) As DFFilter
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    ' 2. 保存筛选列
    If selectCols Is Nothing Then
        Set selectCols = New Collection
    End If

    selectCols.Add c

    ' 3. 返回自身，完成链式调用
    Set selectCol = Me
End Function

' ==================================
' 分组列
' ==================================
' 添加分组列
' @param c 需要添加的列
' @return 返回当前 DFFilter 对象自身，以完成链式调用
Public Function groupBy(ByRef c As IDFFilterCol) As DFFilter
    ' 1. 检查参数
    If c Is Nothing Then
        Err.Raise 10001, Description:="parameter c must be not nothing"
        Exit Function
    End If

    ' 2. 保存筛选列
    If groupByCols Is Nothing Then
        Set groupByCols = UtilsSet.createHashSet
    End If

    groupByCols.Add c

    ' 3. 返回自身，完成链式调用
    Set groupBy = Me
End Function

' ==================================
' 执行筛选
' ==================================
' 执行筛选
' @retur 筛选后数据组成的 DataFrame
Public Function exec() As IDataFrame
    Dim executor As DFFilterExecutor
    Set executor = New DFFilterExecutor
    executor.Init Me, mainConditionExecutor, _
            joinExecutors, selectCols, groupByCols

    Set exec = executor.exec
End Function
